#ifndef MOVES_HPP_
#   define MOVES_HPP_

#   include "Sol.hpp"

#	define USING_CHECK_INSERT4

struct Trio
{
    Trio(unsigned char in1, unsigned char in2, unsigned char in3)
        : first(in1), second(in2), third(in3)
    {}
    
    unsigned char   first;
    unsigned char   second;
    unsigned char   third;
};

class Moves
{
    public:
        typedef enum { INTER = 0, INTRA }                       MoveGlobalType;
        typedef enum { OR_OPT = 0, TWO_OPT_S }                  InterMoveType;
        typedef enum { SWAP = 0 }                               IntraMoveType;
    
        typedef void (*InterMoveFunc) (Vehicle&, Vehicle&, unsigned);
        typedef void (*IntraMoveFunc) (Vehicle&, unsigned);
        
        static void         InterOrOpt(Vehicle& inV1, Vehicle& inV2, unsigned);
        static void         InterTwoOptS(Vehicle& inV1, Vehicle& inV2, unsigned);
        static void         IntraSwap(Vehicle& inV, unsigned);
        
        static InterMoveFunc    InterMoves[TWO_OPT_S + 1];
        static IntraMoveFunc    IntraMoves[SWAP + 1];
        
        static void     buildInstance(VRPTW& inVRPTW);
        static void     delInstance();
        static Moves&   getInstance();
        
        class Move
        {
            public:
                static unsigned NextId;
            
                Move(MoveGlobalType inType)
                    : moType(inType), moId(NextId++)
                {}
                
                virtual ~Move()
                {}
                
                virtual void execute(Sol& inSol, unsigned inSubIndex) = 0;
                
                MoveGlobalType  moType;
                unsigned        moId;
        };
        
        class InterMove : public Move
        {
            public:
                InterMove(InterMoveType inType, unsigned char inV1, unsigned char inV2)
                    : Move(Moves::INTER),
                        imType(inType)
                {
                    imVehicles[0] = inV1;
                    imVehicles[1] = inV2;
                }
            
                InterMoveType           imType;
                unsigned char           imVehicles[2];
            
                void            execute(Sol& inSol, unsigned inSubIndex)
                {   Moves::InterMoves[imType](inSol.getrVehicle(imVehicles[0]), inSol.getrVehicle(imVehicles[1]), inSubIndex); }
        };
            
        class IntraMove : public Move
        {
            public:
                IntraMove(IntraMoveType inType, unsigned char inVehicle)
                    : Move(Moves::INTRA),
                        imType(inType),
                        imVehicle(inVehicle)
                {}
                
                ~IntraMove()    {}
            
                IntraMoveType   imType;
                unsigned char   imVehicle;
            
                void            execute(Sol& inSol, unsigned inSubIndex)
                {   Moves::IntraMoves[imType](inSol.getrVehicle(imVehicle), inSubIndex);    }
        };
        
        protected:
            static Moves* Instance;
            Moves(VRPTW& inVRPTW);
            ~Moves();
        
            VRPTW&      moVRPTW;
            Move****    moMoves;
        
        public:
			unsigned	fillNeighborhoodsBuffer(const Sol& inSol, Moves::Move* inBuffer[], unsigned inMax = 0xFFFFFFFF) const;
            unsigned	getNbNeighborhoods() const
            {	return ((INTRA + 1) * (TWO_OPT_S + 1) * (moVRPTW.size() * moVRPTW.size()));	}
        
};

#endif
